package com.gitee.wsl.text.regex

import com.gitee.wsl.ext.string.builder.replace


fun Regex.Companion.formatBuilder(base:String,block:RegexBuilder.()-> Regex): Regex{
   return RegexBuilder(base).block()
}

/**
 * A builder for [Regex] patterns.
 * @param baseRegex initial pattern
 */
class RegexBuilder(baseRegex: String) {
    private val pattern = StringBuilder(baseRegex)

    /**
     * Adds a reference to the pattern. All the occurrences will be replaced.
     * @param label text to be replaced
     * @param regex new pattern to replace the label with
     * @return this for concatenation
     */
    fun withReference(
        label: String,
        regex: String,
    ) = apply {
        pattern.replace(label, regex)
    }

    /**
     * @return a new [Regex]
     */
    fun build(): Regex = pattern.toString().toRegex()

    /**
     * @return a new [Regex] with the given [option].
     */
    fun build(option: RegexOption): Regex = pattern.toString().toRegex(option)

    /**
     * @return a new [Regex] with the given [options].
     */
    fun build(vararg options: RegexOption): Regex = pattern.toString().toRegex(options.toSet())

    companion object{
        private const val PUNCTUATION_HELPER = "\\p{P}\\p{S}"

        /**
         * @param startDelimiter begin of the match (included)
         * @param endDelimiter end of the match (included)
         * @param strict if `true`, restrictions are applied to the delimeter checks.
         * According to the CommonMark spec:
         * - non-strict means the start delimeter must be left-flanking and end delimeter must be right-flanking
         * - strict means any of the delimeters must not be left and right-flanking at the same time
         *
         * @return a new regex that matches the content between [startDelimiter] and [endDelimiter]
         */
        fun delimiteredPattern(
            startDelimiter: String,
            endDelimiter: String = startDelimiter,
            strict: Boolean,
        ) = RegexBuilder(
            "(?<!start)" +
                    // Start delimiter
                    if (strict) {
                        // If strict, the start delimiter must also not be right-flanking
                        "(?<=^|[\\spunct])(start(?!\\s))"
                    } else {
                        "(start(?![\\spunct])|(?<=^|[\\spunct])start(?!\\s))"
                    } +
                    // Content
                    "(?!start)((.|\\R)+?)" +
                    // End delimiter
                    if (strict) {
                        // If strict, the start delimiter must also not be left-flanking
                        "((?<![\\spunct])end(?=[\\spunct]))"
                    } else {
                        "((?<![\\spunct])end|(?<!\\s)end(?=[\\spunct]))"
                    },
        ).withReference("punct", PUNCTUATION_HELPER)
            .withReference("start", startDelimiter)
            .withReference("end", endDelimiter)
            .build()

    }
}
